home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Text⁄Files / File List 14 / FileSearch.c < prev    next >
Text File  |  1990-09-14  |  21KB  |  696 lines

  1. /*
  2.     FileList 1.4
  3.     "FileSearch.c"
  4.  
  5.     Based on (and compatible with) the FileSearch library that was
  6.     included with "Capp's Prime Editor Construction Kit". Added StuffIt
  7.     (Classic and Deluxe) and Compactor archives support.
  8.  
  9.     The following routines may be used to step through the directories of
  10.     one or more mounted volumes. The volumes may be MFS or HFS and, when
  11.     used properly, can handle both MFS and HFS volumes intermixed without
  12.     a lot of extra checking and can even be used if HFS is not running.
  13.     StuffIt archives can be recognized and be treated as directories.
  14. */
  15.  
  16. #include "FileSearch.h"
  17. #include "Utilities.h"
  18. #include "Main.h"
  19.  
  20. #define STUFFIT        1        /* This is a StuffIt archive */
  21. #define COMPACTOR    2        /* This is a Compactor archive */
  22.  
  23. /* ----- StuffIt compression methods ------------------------------------ */
  24.  
  25. #define noComp        0        /* No compression */
  26. #define rleComp        1        /* RLE compression */
  27. #define lzwComp        2        /* LZW compression */
  28. #define hufComp        3        /* Huffman compression */
  29.  
  30. #define encrypted    0x10    /* Bit set if encrypted */
  31. #define directory    0x20    /* Bit set if this is a directory */
  32. #define stop        0x21    /* End of directory */
  33.  
  34. /* ---- StuffIt archive header ------------------------------------------ */
  35.  
  36. typedef struct {    /* 22 bytes */
  37.     OSType            signature;         /* = 'SIT!' -- for verification */
  38.     unsigned short    numFiles;        /* number of files in archive */
  39.     unsigned long    arcLength;        /* length of entire archive incl. hdr */
  40.     OSType            signature2;        /* = 'rLau' -- for verification */
  41.     unsigned char    version;        /* version number (1 or 2=deluxe) */
  42.     unsigned char    reserved[7];
  43. } sitHdr;
  44.  
  45. /* ----- StuffIt file header -------------------------------------------- */
  46.  
  47. typedef struct {    /* 112 bytes */
  48.     unsigned char    compRMethod;    /* rsrc fork compression method */
  49.     unsigned char    compDMethod;    /* data fork compression method */
  50.     unsigned char    fName[64];        /* a Str63 */
  51.     OSType            fType;            /* file type */
  52.     OSType            fCreator;        /* file creator */
  53.     unsigned short    FndrFlags;        /* copy of Finder flags */
  54.     unsigned long    creationDate;
  55.     unsigned long    modDate;
  56.     unsigned long    rsrcLength;        /* decompressed lengths */
  57.     unsigned long    dataLength;
  58.     unsigned long    compRLength;    /* compressed lengths */
  59.     unsigned long    compDLength;
  60.     unsigned short    rsrcCRC;        /* CRC of rsrc fork */
  61.     unsigned short    dataCRC;        /* CRC of data fork */
  62.     unsigned char    rPad,dPad;        /* pad bytes-valid for encr. entries */
  63.     unsigned char    reserved[4];    /* invalid data for now */
  64.     unsigned short    hdrCRC;            /* CRC of file header */
  65. } fileHdr;
  66.  
  67. /* ----- Compactor archive header --------------------------------------- */
  68.  
  69. typedef struct {    /*     8 bytes */
  70.     unsigned char    version;        /* File format version code (1) */
  71.     unsigned char    part;            /* Segment number */
  72.     unsigned short    arcID;            /* Randomly generated archive ID nbr */
  73.     unsigned long    offset;            /* Directory offset in this segment */
  74. } CompactorHdr;
  75. #define SIZEOF_CompactorHdr        8
  76.  
  77. /* If the file is splitted into more than one segment "offset" is non-zero
  78. only for the last segment, and zero for the other segments. */
  79.  
  80. /* ----- Compactor directory header ------------------------------------- */
  81.  
  82. typedef struct {    /* 7 + note_length bytes (not aligned) */
  83.     unsigned char    filler[4];        /* CRC for directory */
  84.     unsigned char    count[2];        /* Entries (including folders) */
  85.     unsigned char    arcNote[1];        /* Archive note (Pascal string) */
  86. } CompactorDirHdr;
  87. #define SIZEOF_CompactorDirHdr    7    /* sizeof() returns 8 ! */
  88.  
  89. /* ----- Compactor directory entry (after file name) -------------------- */
  90.  
  91. typedef struct {    /* 45 bytes (not aligned) */
  92.     unsigned char    flags;            /* ? */
  93.     unsigned char    data[4];        /* Offset to data */
  94.     unsigned char    type[4];        /* File type */
  95.     unsigned char    creator[4];        /* File creator */
  96.     unsigned char    cdate[4];        /* Creation date */
  97.     unsigned char    mdate[4];        /* Modifiaction date */
  98.     unsigned char    filler[8];        /* ? */
  99.     unsigned char    rsize[4];        /* Resource fork size */
  100.     unsigned char    dsize[4];        /* Data fork size */
  101.     unsigned char    crsize[4];        /* Compressed resource fork size */
  102.     unsigned char    cdsize[4];        /* Compressed data fork size */
  103. } CompactorFile;
  104. #define SIZEOF_CompactorFile    45    /* sizeof() returns 46 ! */
  105.  
  106. /* ----- Static data ---------------------------------------------------- */
  107.  
  108. typedef struct {                    /* Element of nesting stack: */
  109.     long            DirId;            /*  Directory ID */
  110.     short            index;            /*  File index */
  111. } ELEMENT;
  112.  
  113. typedef struct {
  114.     short            MaxLevel;        /* Maximal directory nesting level */
  115.     short            CurrentLevel;    /* Current nesting level */
  116.     short            ArchiveLevel;    /* To set end of archive */
  117.     short            VolumeIndex;    /* Current volume index */
  118.     Str255            VolumeName;        /* Volume name */
  119.     Str255            FileName;        /* File/directory name */
  120.     HVolumeParam    Volume;            /* Volume info */
  121.     CInfoPBRec        Info;            /* File/directory info */
  122.     short            ArchiveRef;        /* Archive file reference */
  123.     short            ArchiveType;    /* Archive type */
  124.     ELEMENT            Stack[];        /* Nesting stack */
  125. } DATA;
  126.  
  127. static DATA *Data = 0;                /* Data block in application heap */
  128.  
  129. /* #define USECRC */    /* Undefine this symbol to gain 622 bytes */
  130. #ifdef USECRC
  131. /*
  132.     CRC computation used by Stuffit.
  133.  
  134.     The logic for this method of calculating the CRC 16 bit polynomial
  135.     is taken from an article by David Schwaderer in the April 1985
  136.     issue of PC Tech Journal.
  137.    
  138.     Not quite CCITT-CRC16... and definitely not xmodem CRC16.
  139. */
  140.  
  141. #define    CrcInit    0x0000    /* Initial CRC value */
  142.  
  143. /* ----- CRC lookup table ----------------------------------------------- */
  144.  
  145. static short crctab[] =    {
  146.     0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
  147.     0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
  148.     0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
  149.     0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
  150.     0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
  151.     0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
  152.     0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
  153.     0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
  154.     0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
  155.     0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
  156.     0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
  157.     0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
  158.     0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
  159.     0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
  160.     0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
  161.     0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
  162.     0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
  163.     0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
  164.     0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
  165.     0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
  166.     0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
  167.     0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
  168.     0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
  169.     0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
  170.     0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
  171.     0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
  172.     0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
  173.     0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
  174.     0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
  175.     0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
  176.     0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
  177.     0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
  178. };
  179.  
  180. /* ----- CRC calculation ------------------------------------------------ */
  181.  
  182. static unsigned short CrcNew (
  183.     register unsigned short crcval,    /* Current CRC value */
  184.     register Byte *buffer,            /* Buffer (or part of) to verify */
  185.     register unsigned long count)    /* Length of buffer */
  186. {
  187.     while (count--)
  188.         crcval = (((crcval >> 8) & 0x00FF) ^
  189.             crctab[(crcval^(*buffer++)) & 0x00FF]);
  190.     return crcval;
  191. }
  192. #endif
  193.  
  194. /* ----- Close archive -------------------------------------------------- */
  195.  
  196. void CloseArchive (void)
  197. {
  198.     register DATA *data = Data;
  199.  
  200.     if (!data || !data->ArchiveRef)
  201.         return;
  202.     FSClose(data->ArchiveRef);
  203.     data->ArchiveRef = data->ArchiveType = 0;
  204. }
  205.  
  206. /* ----- Check for archive ---------------------------------------------- */
  207.  
  208. /*
  209.                                     Type        Creator
  210.  
  211.     StuffIt document                'SIT!'        'SIT!'    
  212.     AutoUnstuffIt application        'APPL'        'aust'
  213.     StuffIt deluxe                    'SITD'        'SIT!'
  214.  
  215.     Compactor document                'PACT'        'CPCT'
  216.     Self-extracting application        'APPL'        'EXTR'
  217. */
  218.  
  219. short IsArchive (register HFileInfo *f)
  220. {
  221.     register DATA *data = Data;
  222.     register short ref;
  223.     register short type;
  224.  
  225.     if (!data || data->ArchiveRef)
  226.         return 0;
  227.  
  228.     /* Check for:
  229.         1.    StuffIt archives (type == 'SIT!' or 'SITD') or AutoUnstuffIt
  230.             applications (type == 'APPL' and creator == 'aust')
  231.         2.     Compactor archives (type == 'PACT') or self-extracting
  232.             applications (type == 'APPL' and creator == 'EXTR') */
  233.  
  234.     if (f->ioFlFndrInfo.fdType == 'SIT!' ||
  235.             f->ioFlFndrInfo.fdType == 'SITD' ||
  236.             (f->ioFlFndrInfo.fdType == 'APPL' &&
  237.             f->ioFlFndrInfo.fdCreator == 'aust')) {
  238.         if (!Stuffit)
  239.             return 0;    /* Not enabled */
  240.         type = STUFFIT;
  241.     } else if (f->ioFlFndrInfo.fdType == 'PACT' ||
  242.             (f->ioFlFndrInfo.fdType == 'APPL' &&
  243.             f->ioFlFndrInfo.fdCreator == 'EXTR')) {
  244.         if (!Compactor)
  245.             return 0;    /* Not enabled */
  246.         type = COMPACTOR;
  247.     } else
  248.         return 0;
  249.  
  250.     /* Open the archive */
  251.  
  252.     {
  253.         HFileParam p;
  254.  
  255.         FillMemory((Byte *)&p, sizeof(p), 0);
  256.         p.ioVRefNum = f->ioVRefNum;
  257.         p.ioDirID = CurrDir();
  258.         p.ioNamePtr = f->ioNamePtr;
  259.         if (PBHOpen(&p, FALSE))
  260.             return 0;
  261.         ref = p.ioFRefNum;
  262.     }
  263.  
  264.     /* Read and verify archive header. Prepare archive walk thru */
  265.  
  266.     switch (type) {
  267.         case STUFFIT:
  268.             {
  269.                 sitHdr sit;
  270.                 long count;
  271.         
  272.                 count = sizeof(sitHdr);
  273.                 if (FSRead(ref, &count, &sit) || GetEOF(ref, &count) ||
  274.                         sit.signature != 'SIT!' ||
  275.                         sit.signature2 != 'rLau' ||
  276.                         (sit.version != 1 && sit.version != 2) ||
  277.                         sit.arcLength != count) {
  278.                     FSClose(ref);
  279.                     return 0;
  280.                 }
  281.             }
  282.             break;
  283.         case COMPACTOR:
  284.             /* Read Compactor header and skip over file data. */
  285.             {
  286.                 CompactorHdr hdr;
  287.                 long count;
  288.  
  289.                 count = SIZEOF_CompactorHdr;
  290.                 if (FSRead(ref, &count, &hdr) ||
  291.                         hdr.version != 0x01 ||
  292.                         !hdr.offset ||
  293.                         SetFPos(ref, fsFromStart, hdr.offset)) {
  294.                     FSClose(ref);
  295.                     return 0;
  296.                 }
  297.                 count = 0;
  298.             }
  299.             /* Read directory header. */
  300.             {
  301.                 CompactorDirHdr hdr;
  302.                 long count;
  303.  
  304.                 count = SIZEOF_CompactorDirHdr;
  305.                 if (FSRead(ref, &count, &hdr) ||
  306.                         SetFPos(ref, fsFromMark, hdr.arcNote[0])) {
  307.                     FSClose(ref);
  308.                     return 0;
  309.                 }
  310.                 ++data->CurrentLevel;
  311.                 data->ArchiveLevel = data->CurrentLevel;
  312.                 BlockMove(hdr.count,
  313.                     &data->Stack[data->CurrentLevel].index, 2);
  314.             }
  315.             break;
  316.         default:
  317.             FSClose(ref);
  318.             return 0;
  319.     }            
  320.  
  321.     data->ArchiveRef = ref;
  322.     return data->ArchiveType = type;
  323. }
  324.  
  325. /* ----- Get next file from StuffIt archive ----------------------------- */
  326.  
  327. static CInfoPBPtr NextStuffitFile (void)
  328. {
  329.     register DATA *data = Data;
  330.     register HFileInfo *f;
  331.     register short err;
  332.     fileHdr file;
  333.     long count;
  334.  
  335.     f = &data->Info.hFileInfo;
  336.  
  337.     /* Read and verify file header */
  338.  
  339.     count = sizeof(fileHdr);
  340.     if (err = FSRead(data->ArchiveRef, &count, &file))
  341.         goto done;    /* eofErr */
  342. #ifdef USECRC
  343.     if (file.hdrCRC !=
  344.             CrcNew(CrcInit, (Byte *)&file, sizeof(fileHdr) - 2)) {
  345.         err = ioErr;
  346.         goto done;
  347.     }
  348. #endif
  349.  
  350.     /* Interprete file header */
  351.  
  352.     switch (file.compRMethod & stop) {
  353.         case directory:    /* New directory */
  354.             f->ioFlAttrib = 0x10;
  355.             f->ioNamePtr = data->FileName;
  356.             BlockMove(file.fName, f->ioNamePtr, file.fName[0] + 1);
  357.             f->ioFlCrDat = file.creationDate;
  358.             f->ioFlMdDat = file.modDate;
  359.             break;
  360.         case stop:        /* End of directory */
  361.             err = fnfErr;
  362.             break;
  363.         default:        /* File */
  364.             /* Skip resource and data fork */
  365.             if (err = SetFPos(data->ArchiveRef, fsFromMark,
  366.                     file.compRLength + file.compDLength))
  367.                 goto done;
  368.             f->ioNamePtr = data->FileName;
  369.             BlockMove(file.fName, f->ioNamePtr, file.fName[0] + 1);
  370.             f->ioFlFndrInfo.fdType = file.fType;
  371.             f->ioFlFndrInfo.fdCreator = file.fCreator;
  372.             f->ioFlFndrInfo.fdFlags = file.FndrFlags;
  373.             f->ioFlCrDat = file.creationDate;
  374.             f->ioFlMdDat = file.modDate;
  375.             f->ioFlLgLen = file.rsrcLength;
  376.             f->ioFlRLgLen = file.dataLength;
  377.             break;
  378.     }
  379. done:
  380.     data->Info.hFileInfo.ioResult = err;
  381.     return &data->Info;
  382. }
  383.  
  384. /* ----- Get next file from Compactor archive --------------------------- */
  385.  
  386. static CInfoPBPtr NextCompactorFile (void)
  387. {
  388.     register DATA *data = Data;
  389.     register HFileInfo *f;
  390.     register short err;
  391.     register Byte buffer[128 + SIZEOF_CompactorFile];
  392.     register Boolean folder;
  393.     long count;
  394.  
  395.     f = &data->Info.hFileInfo;
  396.  
  397.     if (data->CurrentLevel < data->ArchiveLevel) {
  398.         err = ioErr;
  399.         goto done;
  400.     }
  401.  
  402.     /* End of directory ? */
  403.  
  404.     if (data->Stack[data->CurrentLevel].index == 0) {
  405.         err = (data->CurrentLevel == data->ArchiveLevel) ?
  406.             eofErr :    /* End of archive */
  407.             fnfErr;        /* End of folder */
  408.         PopDir();
  409.         goto done;
  410.     }
  411.     for (err = data->ArchiveLevel; err <= data->CurrentLevel; ++err)
  412.         --(data->Stack[err].index);
  413.  
  414.     /* Read file name length. */
  415.  
  416.     count = 1;
  417.     if (err = FSRead(data->ArchiveRef, &count, buffer)) {
  418.         err = ioErr;    /* eofErr */
  419.         goto done;
  420.     }
  421.     folder = (buffer[0] & 0x80) != 0;
  422.     buffer[0] &= 0x7F;
  423.  
  424.     /* Read file name and directory file data */
  425.  
  426.     count = buffer[0] +                /* Name length */
  427.         (folder ?
  428.             2 :                        /* Folder entry */
  429.             SIZEOF_CompactorFile);    /* File entry */
  430.     if (err = FSRead(data->ArchiveRef, &count, buffer + 1)) {
  431.         err = ioErr;    /* eofErr */
  432.         goto done;
  433.     }
  434.  
  435.     /* Copy name. */
  436.  
  437.     BlockMove(buffer, f->ioNamePtr = data->FileName, buffer[0] + 1);
  438.  
  439.     /* Copy directory file entry */
  440.  
  441.     if (folder) {                    /* Folder */
  442.         f->ioFlAttrib = 0x10;
  443.         if (data->CurrentLevel == (data->MaxLevel - 1)) {
  444.             err = memFullErr;
  445.             goto done;
  446.         }
  447.         ++data->CurrentLevel;
  448.         /* Number of entries (including folders) in this folder */
  449.         BlockMove(buffer + buffer[0] + 1,
  450.             &data->Stack[data->CurrentLevel].index, 2);
  451.     } else {                        /* File */
  452.         register CompactorFile *p = (CompactorFile *)(buffer+buffer[0]+1);
  453.         BlockMove(&p->type, &f->ioFlFndrInfo.fdType, sizeof(long));
  454.         BlockMove(&p->creator, &f->ioFlFndrInfo.fdCreator, sizeof(long));
  455.         BlockMove(&p->cdate, &f->ioFlCrDat, sizeof(long));
  456.         BlockMove(&p->mdate, &f->ioFlMdDat, sizeof(long));
  457.         BlockMove(&p->dsize, &f->ioFlLgLen, sizeof(long));
  458.         BlockMove(&p->rsize, &f->ioFlRLgLen, sizeof(long));
  459.     }
  460.  
  461. done:
  462.     data->Info.hFileInfo.ioResult = err;
  463.     return &data->Info;
  464. }
  465.  
  466. /* ----- Get next file from archive ------------------------------------- */
  467.  
  468. CInfoPBPtr NextArchiveFile (void)
  469. {
  470.     register DATA *data = Data;
  471.  
  472.     if (!data || !data->ArchiveRef)
  473.         return 0;
  474.     FillMemory((Byte *)&data->Info.hFileInfo, sizeof(CInfoPBRec), 0);
  475.     switch (data->ArchiveType) {
  476.         case 1:
  477.             return NextStuffitFile();
  478.         case 2:
  479.             return NextCompactorFile();
  480.         default:
  481.             return 0;
  482.     }
  483. }
  484.  
  485. /* ----- Initialize everything ------------------------------------------ */
  486.  
  487. /*
  488.     Allocates data block and an an internal stack for the use of the file
  489.     search routines. Returns TRUE if successful, FALSE otherwise. It must be
  490.     called before calling any other file search routines. Parameter is the
  491.     maximum nesting level of directories. If <= 0 then 128 will be used.
  492. */
  493.  
  494. Boolean FileSearchOpen (register short nLevels)
  495. {
  496.     if (Data)
  497.         return FALSE;    /* Data block already allocated! */
  498.     if (nLevels <= 0)
  499.         nLevels = 128;    /* Use default of 128 nesting levels */
  500.     else
  501.         if (nLevels == 1)
  502.             ++nLevels;    /* At least 2 levels */
  503.     if (Data = (DATA *)NewPtrClear(sizeof(DATA) + nLevels*sizeof(ELEMENT)))
  504.         Data->MaxLevel = nLevels;
  505.     return Data != 0;
  506. }
  507.  
  508. /* ----- Releases the storage allocated by FileSearchOpen --------------- */
  509.  
  510. void FileSearchClose (void)
  511. {
  512.     if (Data) {
  513.         DisposPtr(Data);
  514.         Data = 0;
  515.     }
  516. }
  517.  
  518. /* ----- Prepare volume walk thru --------------------------------------- */
  519.  
  520. /*
  521.     If VRefNum is zero, then things are initialzed internally so that each
  522.     subsequent call to NextVol will return each of the mounted volumes. The
  523.     contents of the HVolumeParam struct will be undefined.  If VRefNum is
  524.     non-zero, then it may be a real VRefNum, a WDDirID, or a drive number.
  525.     If a drive number is given, then the VRefNum will be in ioVRefNum. If
  526.     a WDDirID is given, then the WDDirID will be in ioVRefNum (despite
  527.     HGetVInfo's forcing a real VRefNum to be there; this allows you to,
  528.     say, search all files in or below the current working directory. Note
  529.     that, if HFS is not running, then ioVSigWord will be zero and all
  530.     following fields will be undefined. When you pass a non-zero value
  531.     to FirstVol, calls to NextVol will yield undefined results.
  532. */
  533.  
  534. HVolumeParam *FirstVol (register short VRefNum)
  535. {
  536.     register DATA *data = Data;
  537.  
  538.     if (!data)
  539.         return 0;
  540.     if (VRefNum) {
  541.         data->CurrentLevel = -1;
  542.         data->Volume.ioNamePtr = data->VolumeName;
  543.         data->Volume.ioVRefNum = VRefNum;
  544.         data->Volume.ioVolIndex =
  545.         data->Volume.ioVSigWord = 0;
  546.         PBHGetVInfo(&data->Volume, FALSE);
  547.         if (VRefNum < 0)
  548.             data->Volume.ioVRefNum = VRefNum;
  549.     } else
  550.         data->VolumeIndex = 0;
  551.     return &data->Volume;
  552. }
  553.  
  554. /* ----- Get next volume ------------------------------------------------ */
  555.  
  556. /*
  557.     Returns a pointer to the HVolumeParam info for the next mounted volume.
  558.     If there is no next volume, then the ioResult will contain nsvErr.
  559.     As noted above, ioVSigWord will be zero if HFS is not running; it will
  560.     contain 0xD2D7 if the volume is an MFS volume and HFS is running, and
  561.     it will contain 0x4244 if it is an HFS volume.
  562. */
  563.  
  564. HVolumeParam *NextVol (void)
  565. {
  566.     register DATA *data = Data;
  567.  
  568.     if (!data)
  569.         return 0;
  570.     data->CurrentLevel = -1;
  571.     data->Volume.ioNamePtr = data->VolumeName;
  572.     data->Volume.ioVolIndex = ++(data->VolumeIndex);
  573.     data->Volume.ioVSigWord = 0;
  574.     PBHGetVInfo(&data->Volume, FALSE);
  575.     return &data->Volume;
  576. }
  577.  
  578. /* ----- Prepare file walk thru ----------------------------------------- */
  579.  
  580. /*
  581.     Initializes things internally so that each subsequent call to NextFile
  582.     will return each file/directory in the given volume/directory.
  583.     If DirID is zero, then the root directory of the volume last selected
  584.     with FirstVol or NextVol is used. If DirID is non-zero, then the
  585.     directory with that ID on the volume last selected with FirstVol or
  586.     NextVol will be used (if the volume is MFS, then the DirID will be
  587.     ignored). If FirstFile has been called before for the current
  588.     volume, then the current status of the search for that directory is
  589.     saved before starting the new directory search. The saved status may
  590.     be restored with PopDir (see below). Note that searches can be nested
  591.     no deeper than 128 levels. If an attempt is made to go beyond that,
  592.     then FirstFile will ignore it and the next call to NextFile will return
  593.     the next file at the current level.
  594. */
  595.  
  596. void FirstFile (register long DirID)
  597. {
  598.     register DATA *data = Data;
  599.     register ELEMENT *p;
  600.  
  601.     if (!data || data->CurrentLevel == (data->MaxLevel - 1))
  602.         return;
  603.     p = data->Stack + ++(data->CurrentLevel);
  604.     p->DirId = IsHFSVol(data->Volume) ? DirID : 0;
  605.     p->index = 0;
  606. }
  607.  
  608. /* ----- Get next file -------------------------------------------------- */
  609.  
  610. /*
  611.     Returns a pointer to the file info for the next file in the current
  612.     directory. If there is no next file, then the ioResult will contain
  613.     fnfErr. If the current volume is an MFS volume, then the pointer
  614.     returned will actually be a pointer to a FileParam struct, which is
  615.     a subset of the CInfoPBRec struct.
  616. */
  617.  
  618. CInfoPBPtr NextFile (void)
  619. {
  620.     register DATA *data = Data;
  621.     register ELEMENT *p;
  622.     register HFileInfo *f;
  623.  
  624.     if (!data)
  625.         return 0;
  626.     f = &data->Info.hFileInfo;
  627.     p = data->Stack + data->CurrentLevel;
  628.     f->ioNamePtr = data->FileName;
  629.     f->ioVRefNum = data->Volume.ioVRefNum;
  630.     f->ioFDirIndex = ++(p->index);
  631.     f->ioDirID = p->DirId;
  632.     if (IsHFSVol(data->Volume))
  633.         PBGetCatInfo(&data->Info, FALSE);
  634.     else {
  635.         PBGetFInfo(&data->Info, FALSE);
  636.         f->ioFlAttrib &= 0xEF;
  637.     }
  638.     return &data->Info;
  639. }
  640.  
  641. /* ----- Current volume ------------------------------------------------- */
  642.  
  643. /*
  644.     Returns the vRefNum of the volume last returned by FirstVol or NextVol.
  645. */
  646. /*
  647. short CurrVol (void)
  648. {
  649.     return Data ? Data->Volume.ioVRefNum : 0;
  650. }
  651. */
  652. /* ----- Current level -------------------------------------------------- */
  653.  
  654. /*
  655.     Returns the number of "saved" directory searches; i.e., the level of
  656.     directory nesting, unless you've skipped one or more levels
  657.     deliberately.
  658. */
  659. /*
  660. short CurrLevel (void)
  661. {
  662.     return Data ? Data->CurrentLevel : 0;
  663. }
  664. */
  665. /* ----- Current directory ---------------------------------------------- */
  666.  
  667. /*
  668.     Returns the DirID of the directory containing the file last returned by
  669.     NextFile. If the file is on an MFS volume, then a zero will be returned.
  670. */
  671.  
  672. long CurrDir (void)
  673. {
  674.     register DATA *data = Data;
  675.  
  676.     return data ? data->Stack[data->CurrentLevel].DirId : 0;
  677. }
  678.  
  679. /* ----- Pop directory -------------------------------------------------- */
  680.  
  681. /*
  682.     Restores the most recently saved search status (saved with FirstFile).
  683.     If there are no saved searches left, PopDir returns FALSE; otherwise, it
  684.     returns TRUE.
  685. */
  686.  
  687. Boolean PopDir (void)
  688. {
  689.     register DATA *data = Data;
  690.  
  691.     if (!data || !data->CurrentLevel)
  692.         return FALSE;
  693.     --(data->CurrentLevel);
  694.     return TRUE;
  695. }
  696.